﻿using CommerceML.Version205;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace EDEX_1C2CommerceML
{
    /// <summary>
    /// Взаимодействие с базой 1С версии 7.7
    /// </summary>
    public class _1Cv77 : IDisposable, I1C
    {
        private string connectionString { get; set; }
        private dynamic instance { get; set; }
        private dynamic db { get; set; }

        /// <summary>
        /// Инициализация подключения
        /// </summary>
        /// <param name="connectionString">Строка подключения к базе 1С</param>
        public _1Cv77(string connectionString)
        {
            this.connectionString = connectionString;

            var type = Type.GetTypeFromProgID("V77.ComConnector");
            instance = Activator.CreateInstance(type);
            instance.PoolCapacity = 10;
            instance.PoolTimeout = 60;
            instance.MaxConnections = 2;

            db = instance.Connect(this.connectionString);
        }

        /// <summary>
        /// Получение журнала изменений
        /// </summary>
        /// <param name="startJournalDate">Дата начала периода выгрузки журнала</param>
        /// <returns>Путь к файлу выгрузки</returns>
        private string GetJournal(DateTime startJournalDate, DateTime endJouranalDate)
        {
            string path = System.IO.Path.Combine(
                System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location),
                "EvntLog");

            string filename = System.IO.Path.Combine(
                path,
                endJouranalDate.ToString("yyyyMMddhhmmss") + ".xml");

            if (!System.IO.Directory.Exists(path))
                System.IO.Directory.CreateDirectory(path);

            dynamic filter = db.NewObject("Структура");
            filter.Вставить("ДатаНачала", startJournalDate);
            filter.Вставить("ДатаОкончания", endJouranalDate);

            dynamic evntArray = db.NewObject("Массив");
            evntArray.Добавить("_$Data$_.Post");  //Данные Проведение
            filter.Вставить("Событие", evntArray);

            dynamic metaArray = db.NewObject("Массив");
            metaArray.Добавить(db.Метаданные.Документы.РеализацияТоваровУслуг);
            filter.Вставить("Метаданные", metaArray);

            db.ВыгрузитьЖурналРегистрации(filename, filter);

            return filename;
        }

        /// <summary>
        /// Получение перечисления идентификаторов измененных документов из файла журнала
        /// </summary>
        /// <param name="path">Путь к файлу журнала</param>
        /// <returns></returns>
        private IEnumerable<string> GetJournalItems(string path)
        {
            var xml = new System.Xml.XmlDocument();
            xml.Load(path);

            System.Xml.XmlNamespaceManager nsmgr = new System.Xml.XmlNamespaceManager(xml.NameTable);
            nsmgr.AddNamespace("v8e", xml.DocumentElement.NamespaceURI);
            return xml.DocumentElement
                .SelectNodes("//v8e:Data", nsmgr)
                .Cast<System.Xml.XmlNode>()
                .Select(r => r.InnerText);
        }

        public dynamic GetDocumentById(string id)
        {
            return this.db.Документы.РеализацияТоваровУслуг.ПолучитьСсылку(this.db.NewObject("УникальныйИдентификатор", id));
        }

        public IEnumerable<dynamic> GetDocumentsByDay(int day)
        {
            string txtQuery =
                @"ВЫБРАТЬ
                    РеализацияТоваровУслуг.Ссылка
		        ИЗ
                    Документ.РеализацияТоваровУслуг КАК РеализацияТоваровУслуг
                ГДЕ
                    1=1
                    И РеализацияТоваровУслуг.Проведен = True
                    И РеализацияТоваровУслуг.Дата >= &Дни";

            dynamic query = db.NewObject("Запрос", txtQuery);
            query.УстановитьПараметр("Дни", DateTime.Today.AddDays(day * -1)); 
            dynamic qResult = query.Выполнить().Выбрать();

            while (qResult.Следующий())
                yield return this.GetDocumentById(db.XMLСтрока(qResult.Ссылка.УникальныйИдентификатор()));
        }

        public IEnumerable<dynamic> GetDocumentsByPeriod(DateTime startDate, DateTime endDate)
        {
            var fileName = this.GetJournal(startDate, endDate);
            var items = this.GetJournalItems(fileName);

            if (System.IO.File.Exists(fileName))
                System.IO.File.Delete(fileName);

            foreach (var id in items)
                yield return this.GetDocumentById(id);
        }

        public КоммерческаяИнформация GetCommerceML(dynamic doc)
        {
            КоммерческаяИнформация document = new КоммерческаяИнформация();
            document.ВерсияСхемы = "2.05";
            document.ДатаФормирования = DateTime.Now;

            Документ entity = new Документ();

            entity.Ид = this.db.XMLСтрока(doc.Ссылка.УникальныйИдентификатор());
            entity.Дата = ((DateTime)(doc.Ссылка.Дата));
            entity.ХозОперация = ХозОперацияТип.Отпусктовара;
            entity.Номер = doc.Ссылка.Номер;
            entity.Роль = РольТип.Продавец;
            entity.Валюта = doc.Ссылка.Валюта.Наименование; //doc.Ссылка.ВалютаДокумента.Наименование;
            //вообще исчезло, видимо в 8.2 самому нужно курс искать 
            //entity.Курс = Convert.ToString(doc.Ссылка.КурсВзаиморасчетов);
            entity.Сумма = Convert.ToDecimal(doc.Ссылка.СуммаДокумента);

            ДокументКонтрагент seller = new ДокументКонтрагент();
            seller.Ид = this.db.XMLСтрока(doc.Ссылка.Организация.Ссылка.УникальныйИдентификатор());
            seller.Наименование = doc.Ссылка.Организация.Наименование;
            seller.Роль = РольТип.Продавец;

            ДокументКонтрагент buyer = new ДокументКонтрагент();
            buyer.Ид = this.db.XMLСтрока(doc.Ссылка.Контрагент.Ссылка.УникальныйИдентификатор());
            buyer.Наименование = doc.Ссылка.Контрагент.Наименование;
            buyer.Роль = РольТип.Покупатель;

            if (this.db.XMLСтрока(doc.Ссылка.Грузополучатель.Ссылка.УникальныйИдентификатор()) != "00000000-0000-0000-0000-000000000000")
            {
                buyer.Склад.Ид = this.db.XMLСтрока(doc.Ссылка.Грузополучатель.Ссылка.УникальныйИдентификатор());
                buyer.Склад.Наименование = doc.Ссылка.Грузополучатель.Наименование;
            }

            entity.Контрагенты = new ДокументКонтрагент[] { seller, buyer };

            //entity.Время = ((DateTime)(doc.Ссылка.Дата));
            entity.Время = ((String)(doc.Ссылка.Дата));
            entity.Комментарий = doc.Комментарий;

            List<ДокументТовар> products = new List<ДокументТовар>();
            Decimal TotalTax = 0;
            foreach (var itemDoc in doc.Товары)
            {
                ДокументТовар product = new ДокументТовар();
                product.Ид = this.db.XMLСтрока(itemDoc.Номенклатура.Ссылка.УникальныйИдентификатор());
                product.Артикул = itemDoc.Номенклатура.Артикул;
                product.Наименование = itemDoc.Номенклатура.Наименование;

                /* 8.1 
                product.БазоваяЕдиница = new ТоварБазоваяЕдиница
                {
                    Код = itemDoc.Номенклатура.БазоваяЕдиницаИзмерения.Код,
                    НаименованиеПолное = itemDoc.Номенклатура.БазоваяЕдиницаИзмерения.НаименованиеПолное,
                    МеждународноеСокращение = itemDoc.Номенклатура.БазоваяЕдиницаИзмерения.МеждународноеСокращение
                };
                ТоварБазоваяЕдиницаПересчет bu = new ТоварБазоваяЕдиницаПересчет();
                bu.Единица = itemDoc.ЕдиницаИзмерения.Наименование;
                bu.Коэффициент = Convert.ToString(itemDoc.ЕдиницаИзмерения.Коэффициент);
                 */

                product.БазоваяЕдиница = new ТоварБазоваяЕдиница
                {
                    Код = itemDoc.Номенклатура.ЕдиницаИзмерения.Код,
                    НаименованиеПолное = itemDoc.Номенклатура.ЕдиницаИзмерения.НаименованиеПолное,
                    МеждународноеСокращение = itemDoc.Номенклатура.ЕдиницаИзмерения.МеждународноеСокращение
                };
                ТоварБазоваяЕдиницаПересчет bu = new ТоварБазоваяЕдиницаПересчет();
                bu.Единица = itemDoc.Упаковка.ЕдиницаИзмерения.Наименование;
                bu.Коэффициент = Convert.ToString(itemDoc.Упаковка.Коэффициент);

                /* 8.1
                ЗначениеРеквизита valueWeight = new ЗначениеРеквизита();
                valueWeight.Наименование = "Вес";
                valueWeight.Значение = new string[] { Convert.ToString(itemDoc.ЕдиницаИзмерения.Вес) }; 
                ЗначениеРеквизита valueVolume = new ЗначениеРеквизита();
                valueVolume.Наименование = "Объем";
                valueVolume.Значение = new string[] { Convert.ToString(itemDoc.ЕдиницаИзмерения.Объем) };
                bu.ДополнительныеДанные = new ЗначениеРеквизита[] { valueWeight, valueVolume };
                product.БазоваяЕдиница.Пересчет = new ТоварБазоваяЕдиницаПересчет[] { bu };

                 */

                ЗначениеРеквизита valueWeight = new ЗначениеРеквизита();
                valueWeight.Наименование = "Вес";
                valueWeight.Значение = new string[] { Convert.ToString(itemDoc.Упаковка.Вес) };
                ЗначениеРеквизита valueVolume = new ЗначениеРеквизита();
                valueVolume.Наименование = "Объем";
                valueVolume.Значение = new string[] { Convert.ToString(itemDoc.Упаковка.Объем) };
                bu.ДополнительныеДанные = new ЗначениеРеквизита[] { valueWeight, valueVolume };
                product.БазоваяЕдиница.Пересчет = new ТоварБазоваяЕдиницаПересчет[] { bu };


                ТоварСтавкаНалога taxRate = new ТоварСтавкаНалога();
                taxRate.Наименование = "НДС";
                string taxName = this.db.XMLСтрока(itemDoc.СтавкаНДС);
                switch (taxName)
                {
                    case "НДС18":
                        taxRate.Ставка = "18%";
                        break;
                }
                product.СтавкиНалогов = new ТоварСтавкаНалога[] { taxRate };

                //ВыгрузитьДопИнформациюОНоменклатуре(ОбъектXML, Товар);
                ЗначениеРеквизита param1 = new ЗначениеРеквизита { Значение = new string[] { Convert.ToString(itemDoc.Номенклатура.ВидНоменклатуры.Наименование) }, Наименование = "ВидНоменклатуры" };
                ЗначениеРеквизита param2 = new ЗначениеРеквизита { Значение = new string[] { this.db.XMLСтрока(itemDoc.Номенклатура.ВидНоменклатуры.ТипНоменклатуры) }, Наименование = "ТипНоменклатуры" };
                ЗначениеРеквизита param3 = new ЗначениеРеквизита { Значение = new string[] { Convert.ToString(itemDoc.Номенклатура.Наименование) }, Наименование = "НаименованиеКраткое" };
                ЗначениеРеквизита param4 = new ЗначениеРеквизита { Значение = new string[] { Convert.ToString(itemDoc.Номенклатура.НаименованиеПолное) }, Наименование = "НаименованиеПолное" };
                product.ЗначенияРеквизитов = new ЗначениеРеквизита[] { param1, param2, param3, param4 };

                //ВыгрузитьБазовыеРеквизитыТабличнойЧастиТоваров
                product.ИдКаталога = this.db.XMLСтрока(doc.Ссылка.Организация.Ссылка.УникальныйИдентификатор());
                product.ЦенаЗаЕдиницу = Convert.ToDecimal(itemDoc.Цена);
                product.ЦенаЗаЕдиницуSpecified = true;
                product.Количество = itemDoc.Количество;
                product.КоличествоSpecified = true;
                product.Сумма = Convert.ToDecimal(itemDoc.Сумма);

                /* 8.1
                product.Единица = Convert.ToString(itemDoc.ЕдиницаИзмерения.Коэффициент);
                product.Коэффициент = Convert.ToString(itemDoc.Коэффициент);
                 */
                product.Единица = Convert.ToString(itemDoc.Упаковка.ЕдиницаИзмерения.Наименование);
                product.Коэффициент = Convert.ToString(itemDoc.Упаковка.Коэффициент);

                product.Сумма = Convert.ToDecimal(itemDoc.Сумма);
                product.СуммаSpecified = true;

                //ВыгрузитьИнформациюОНалогахДляВыборки
                ДокументТоварНалог itemTax = new ДокументТоварНалог();
                itemTax.Наименование = "НДС";

                /* 8.1
                itemTax.УчтеноВСумме = doc.Ссылка.СуммаВключаетНДС;
                */
                itemTax.УчтеноВСумме = doc.Ссылка.ЦенаВключаетНДС;

                itemTax.Сумма = Convert.ToDecimal(itemDoc.СуммаНДС);
                TotalTax = TotalTax + itemTax.Сумма;
                product.Налоги = new ДокументТоварНалог[] { itemTax };

                products.Add(product);
            }

            entity.Товары = products.ToArray();

            ДокументНалог taxDoc = new ДокументНалог();
            taxDoc.Наименование = "НДС";

            /* 8.1
            taxDoc.УчтеноВСумме = doc.Ссылка.СуммаВключаетНДС;
             */
            taxDoc.УчтеноВСумме = doc.Ссылка.ЦенаВключаетНДС;

            taxDoc.Сумма = TotalTax;
            entity.Налоги = new ДокументНалог[] { taxDoc };

            document.Items = new[] { entity };

            return document;
        }

        public Dictionary<string, string> GetSuppliers()
        {
            string txtQuery =
                @"ВЫБРАТЬ
                      Контрагенты.Ссылка
                  ИЗ
                      Справочник.Контрагенты КАК Контрагенты
                  УПОРЯДОЧИТЬ ПО
                      Контрагенты.Наименование";

            dynamic query = db.NewObject("Запрос", txtQuery);
            dynamic qResult = query.Выполнить().Выбрать();

            var data = new Dictionary<string, string>();

            while (qResult.Следующий())
                data.Add(
                    db.XMLСтрока(qResult.Ссылка.УникальныйИдентификатор()),
                    qResult.Ссылка.Наименование);

            return data;
        }

        public void Dispose()
        {
            System.Runtime.InteropServices.Marshal.ReleaseComObject(this.db);
            System.Runtime.InteropServices.Marshal.ReleaseComObject(this.instance);  
        }
    }
}
